/*****************************************************************
*
*	Netcare Agent Project
*		Copyright(C) 2010, Wicresoft EKA. All Rights Reserved.
*
*	Authors:
*		Parker Zhou (parkerz@wicresoft.com)
*
*	Part of the product code depends on gSOAP under GPL license
*		Copyright(C) 2000-2010, Robert van Engelen, Genivia Inc. All Rights Reserved.
*
*****************************************************************/


#include "Serviceoperation.h"

#ifdef WIN32
#include <stdio.h>
#else
// Linux
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <fstream>
#include <iostream>
#endif


NETCARE_AGENT;
USING_STD;

#ifdef WIN32
CSysService::CSysService(int pid , ServiceStatus status , const wchar_t* shortName ,const wchar_t* displayName)
{
	this->PID = pid;
	this->Status = status;
	this->ShortName.append(shortName);
	this->DisplayName.append(displayName);
}
CSysService::CSysService(const wchar_t* shortName)
{
	this->PID = -1;
	this->Status = ServiceStatus::service_unknow;
	this->ShortName.append(shortName);
}
#else
CSysService::CSysService(int pid , ServiceStatus status , const char* shortName ,const char* displayName)
{
	this->PID = pid;
	this->Status = status;
	this->ShortName.append(shortName);
	this->DisplayName.append(displayName);
}
CSysService::CSysService(const char* shortName)
{
	this->PID = -1;
	this->Status = service_unknow;
	this->ShortName.append(shortName);
}
#endif

CSysService::~CSysService()
{
	
}

CServiceOperation::CServiceOperation(void)
{
}

CServiceOperation::~CServiceOperation(void)
{
}

vector<CSysService*>* CServiceOperation::GetServices()
{
#ifdef WIN32
	SC_HANDLE scHandle;
	DWORD cbBytesNeeded;
	DWORD cbServicesReturned;
	DWORD cbResumeHandle = 0;
	
	//http://msdn.microsoft.com/en-us/library/ms684323(VS.85).aspx
	scHandle = OpenSCManagerW(L".", SERVICES_ACTIVE_DATABASE, SC_MANAGER_ENUMERATE_SERVICE);
	vector<CSysService*>* list = NULL;
	if(scHandle)
	{
		//http://msdn.microsoft.com/en-us/library/ms682640(VS.85).aspx
		if(!EnumServicesStatusExW(scHandle, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, 
				NULL, 0, &cbBytesNeeded, &cbServicesReturned, &cbResumeHandle, NULL)
			&& GetLastError() == ERROR_MORE_DATA)
		{
			LPVOID lpServices = malloc(cbBytesNeeded);
			cbResumeHandle = 0;

			if(EnumServicesStatusExW(scHandle, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, 
				(LPBYTE)lpServices, cbBytesNeeded, &cbBytesNeeded, &cbServicesReturned, &cbResumeHandle, NULL))
			{
				LPENUM_SERVICE_STATUS_PROCESS p = (LPENUM_SERVICE_STATUS_PROCESS)lpServices;
				list = new vector<CSysService*>();
				for(int i=0; i<(int)cbServicesReturned; i++, p++)
				{
					list->push_back(new CSysService(
						(int)p->ServiceStatusProcess.dwProcessId,
						ConvertStatus(p->ServiceStatusProcess.dwCurrentState),
						p->lpServiceName,
						p->lpDisplayName));
				}
			}
			free(lpServices);
		}
		CloseServiceHandle(scHandle);
	}
	return list;
#else
	int i;
	DIR *dir;
	vector<CSysService*>* list = new vector<CSysService*>();
	struct dirent* entry;
	dir = opendir("/etc/init.d");
	if( dir == NULL )
		return NULL;
	while( ( entry = readdir(dir) ) != NULL )
	{
		for( i = 0 ; i < excludedNumber ; i++ )
		{
			if( entry->d_name[0] == '.' || !strcmp ( entry->d_name , excluded[i] ) )
				break;
		}
		//don't exclude
		if( i == excludedNumber )
			list->push_back(new CSysService(entry->d_name));
	}
	closedir(dir);
	return list;
#endif
}


#ifdef WIN32
bool CServiceOperation::DoStartSvc(LPCWSTR szSvcName)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
    SERVICE_STATUS_PROCESS ssStatus; 
    DWORD dwOldCheckPoint; 
    DWORD dwStartTickCount;
    DWORD dwWaitTime;
    DWORD dwBytesNeeded;

    // Get a handle to the SCM database. 
 
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return false;
    }

    // Get a handle to the service.

    schService = OpenService( 
        schSCManager,         // SCM database 
        szSvcName,            // name of service 
        SERVICE_ALL_ACCESS);  // full access 
 
    if (schService == NULL)
    { 
        printf("OpenService failed (%d)\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return false;
    }    

    // Check the status in case the service is not stopped. 

    if (!QueryServiceStatusEx( 
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // size needed if buffer is too small
    {
        printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return false; 
    }

    // Check if the service is already running. It would be possible 
    // to stop the service here, but for simplicity this example just returns. 

    if(ssStatus.dwCurrentState != SERVICE_STOPPED && ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
    {
        printf("Cannot start the service because it is already running\n");
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return false; 
    }

    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    // Wait for the service to stop before attempting to start it.

    while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
    {
        // Do not wait longer than the wait hint. A good interval is 
        // one-tenth of the wait hint but not less than 1 second  
        // and not more than 10 seconds. 
 
        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status until the service is no longer stop pending. 
 
        if (!QueryServiceStatusEx( 
                schService,                     // handle to service 
                SC_STATUS_PROCESS_INFO,         // information level
                (LPBYTE) &ssStatus,             // address of structure
                sizeof(SERVICE_STATUS_PROCESS), // size of structure
                &dwBytesNeeded ) )              // size needed if buffer is too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
            CloseServiceHandle(schService); 
            CloseServiceHandle(schSCManager);
            return false; 
        }

        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                printf("Timeout waiting for service to stop\n");
                CloseServiceHandle(schService); 
                CloseServiceHandle(schSCManager);
                return false; 
            }
        }
    }

    // Attempt to start the service.

    if (!StartService(
            schService,  // handle to service 
            0,           // number of arguments 
            NULL) )      // no arguments 
    {
        printf("StartService failed (%d)\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return false; 
    }
    //else printf("Service start pending...\n"); 

    // Check the status until the service is no longer start pending. 
 
    if (!QueryServiceStatusEx( 
            schService,                     // handle to service 
            SC_STATUS_PROCESS_INFO,         // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
    {
        printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
        CloseServiceHandle(schService); 
        CloseServiceHandle(schSCManager);
        return false; 
    }
 
    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    while (ssStatus.dwCurrentState == SERVICE_START_PENDING) 
    { 
        // Do not wait longer than the wait hint. A good interval is 
        // one-tenth the wait hint, but no less than 1 second and no 
        // more than 10 seconds. 
 
        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status again. 
 
        if (!QueryServiceStatusEx( 
            schService,             // handle to service 
            SC_STATUS_PROCESS_INFO, // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
            break; 
        }
 
        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                // No progress made within the wait hint.
                break;
            }
        }
    } 

    // Determine whether the service is running.

    if (ssStatus.dwCurrentState == SERVICE_RUNNING) 
    {
        printf("Service started successfully.\n"); 
		return true;
    }
    else 
    { 
        printf("Service not started. \n");
        printf("  Current State: %d\n", ssStatus.dwCurrentState); 
        printf("  Exit Code: %d\n", ssStatus.dwWin32ExitCode); 
        printf("  Check Point: %d\n", ssStatus.dwCheckPoint); 
        printf("  Wait Hint: %d\n", ssStatus.dwWaitHint); 
		return false;
    } 

    CloseServiceHandle(schService); 
    CloseServiceHandle(schSCManager);
}
bool  CServiceOperation::DoStopSvc(LPCWSTR szSvcName)
{
	SC_HANDLE schSCManager;
	SC_HANDLE schService;
    SERVICE_STATUS_PROCESS ssp;
    DWORD dwStartTime = GetTickCount();
    DWORD dwBytesNeeded;
    DWORD dwTimeout = 30000; // 30-second time-out
    DWORD dwWaitTime;
	bool flag = true;
    // Get a handle to the SCM database. 
 
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // ServicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return false;
    }

    // Get a handle to the service.

    schService = OpenService( 
        schSCManager,         // SCM database 
        szSvcName,            // name of service 
        SERVICE_STOP | 
        SERVICE_QUERY_STATUS | 
        SERVICE_ENUMERATE_DEPENDENTS);  
 
    if (schService == NULL)
    { 
        printf("OpenService failed (%d)\n", GetLastError()); 
        CloseServiceHandle(schSCManager);
        return false;
    }    

    // Make sure the service is not already stopped.

    if ( !QueryServiceStatusEx( 
            schService, 
            SC_STATUS_PROCESS_INFO,
            (LPBYTE)&ssp, 
            sizeof(SERVICE_STATUS_PROCESS),
            &dwBytesNeeded ) )
    {
        printf("QueryServiceStatusEx failed (%d)\n", GetLastError()); 
		flag = false;
        goto stop_cleanup;
    }

    if ( ssp.dwCurrentState == SERVICE_STOPPED )
    {
        printf("Service is already stopped.\n");
        goto stop_cleanup;
    }

    // If a stop is pending, wait for it.

    while ( ssp.dwCurrentState == SERVICE_STOP_PENDING ) 
    {
        printf("Service stop pending...\n");

        // Do not wait longer than the wait hint. A good interval is 
        // one-tenth of the wait hint but not less than 1 second  
        // and not more than 10 seconds. 
 
        dwWaitTime = ssp.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        if ( !QueryServiceStatusEx( 
                 schService, 
                 SC_STATUS_PROCESS_INFO,
                 (LPBYTE)&ssp, 
                 sizeof(SERVICE_STATUS_PROCESS),
                 &dwBytesNeeded ) )
        {
            printf("QueryServiceStatusEx failed (%d)\n", GetLastError()); 
			flag = false;
            goto stop_cleanup;
        }

        if ( ssp.dwCurrentState == SERVICE_STOPPED )
        {
            printf("Service stopped successfully.\n");
            goto stop_cleanup;
        }

        if ( GetTickCount() - dwStartTime > dwTimeout )
        {
            printf("Service stop timed out.\n");
			flag = false;
            goto stop_cleanup;
        }
    }

    // If the service is running, dependencies must be stopped first.

    StopDependentServices(schSCManager,schService);

    // Send a stop code to the service.

    if ( !ControlService( 
            schService, 
            SERVICE_CONTROL_STOP, 
            (LPSERVICE_STATUS) &ssp ) )
    {
        printf( "ControlService failed (%d)\n", GetLastError() );
		flag = false;
        goto stop_cleanup;
    }

    // Wait for the service to stop.

    while ( ssp.dwCurrentState != SERVICE_STOPPED ) 
    {
        Sleep( 3000 );
        if ( !QueryServiceStatusEx( 
                schService, 
                SC_STATUS_PROCESS_INFO,
                (LPBYTE)&ssp, 
                sizeof(SERVICE_STATUS_PROCESS),
                &dwBytesNeeded ) )
        {
            printf( "QueryServiceStatusEx failed (%d)\n", GetLastError() );
			flag = false;
            goto stop_cleanup;
        }

        if ( ssp.dwCurrentState == SERVICE_STOPPED )
            break;

        if ( GetTickCount() - dwStartTime > dwTimeout )
        {
            printf( "Wait timed out\n" );
			flag = false;
            goto stop_cleanup;
        }
    }
    printf("Service stopped successfully\n");

stop_cleanup:
    CloseServiceHandle(schService); 
    CloseServiceHandle(schSCManager);
	return flag;
}
bool CServiceOperation::StopDependentServices(SC_HANDLE schSCManager,SC_HANDLE schService)
{
	DWORD i;
    DWORD dwBytesNeeded;
    DWORD dwCount;

    LPENUM_SERVICE_STATUS   lpDependencies = NULL;
    ENUM_SERVICE_STATUS     ess;
    SC_HANDLE               hDepService;
    SERVICE_STATUS_PROCESS  ssp;

    DWORD dwStartTime = GetTickCount();
    DWORD dwTimeout = 30000; // 30-second time-out

    // Pass a zero-length buffer to get the required buffer size.
    if ( EnumDependentServices( schService, SERVICE_ACTIVE, 
         lpDependencies, 0, &dwBytesNeeded, &dwCount ) ) 
    {
         // If the Enum call succeeds, then there are no dependent
         // services, so do nothing.
         return true;
    } 
    else 
    {
        if ( GetLastError() != ERROR_MORE_DATA )
            return false; // Unexpected error

        // Allocate a buffer for the dependencies.
        lpDependencies = (LPENUM_SERVICE_STATUS) HeapAlloc( 
            GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded );
  
        if ( !lpDependencies )
            return false;

        __try {
            // Enumerate the dependencies.
            if ( !EnumDependentServices( schService, SERVICE_ACTIVE, 
                lpDependencies, dwBytesNeeded, &dwBytesNeeded,
                &dwCount ) )
            return false;

            for ( i = 0; i < dwCount; i++ ) 
            {
                ess = *(lpDependencies + i);
                // Open the service.
                hDepService = OpenService( schSCManager, 
                   ess.lpServiceName, 
                   SERVICE_STOP | SERVICE_QUERY_STATUS );

                if ( !hDepService )
                   return false;

                __try {
                    // Send a stop code.
                    if ( !ControlService( hDepService, 
                            SERVICE_CONTROL_STOP,
                            (LPSERVICE_STATUS) &ssp ) )
                    return false;

                    // Wait for the service to stop.
                    while ( ssp.dwCurrentState != SERVICE_STOPPED ) 
                    {
                        Sleep( ssp.dwWaitHint );
                        if ( !QueryServiceStatusEx( 
                                hDepService, 
                                SC_STATUS_PROCESS_INFO,
                                (LPBYTE)&ssp, 
                                sizeof(SERVICE_STATUS_PROCESS),
                                &dwBytesNeeded ) )
                        return false;

                        if ( ssp.dwCurrentState == SERVICE_STOPPED )
                            break;

                        if ( GetTickCount() - dwStartTime > dwTimeout )
                            return false;
                    }
                } 
                __finally 
                {
                    // Always release the service handle.
                    CloseServiceHandle( hDepService );
                }
            }
        } 
        __finally 
        {
            // Always free the enumeration buffer.
            HeapFree( GetProcessHeap(), 0, lpDependencies );
        }
    } 
    return true;
}

ServiceStatus CServiceOperation::ConvertStatus(DWORD dwCurrentState)
{
	switch(dwCurrentState){
		case SERVICE_STOPPED:
			return ServiceStatus::service_stop;
		case SERVICE_START_PENDING:
			return ServiceStatus::service_start_pending;
		case SERVICE_STOP_PENDING:
			return ServiceStatus::service_stop_pending;
		case SERVICE_RUNNING:
			return ServiceStatus::service_running;
		case SERVICE_CONTINUE_PENDING:
			return ServiceStatus::service_continue_pending;
		case SERVICE_PAUSE_PENDING:
			return ServiceStatus::service_pause_pending;
		case SERVICE_PAUSED:
			return ServiceStatus::service_pause;
		default:
			return ServiceStatus::service_unknow;
	}
}


bool CServiceOperation::RestartService(LPCWSTR aServiceName)
{
	
	if(DoStopSvc(aServiceName))
		return DoStartSvc(aServiceName);
	else
		return false;
}

#else

bool CServiceOperation::RestartService(string aServiceName)
{
	string command("service ");
	command.append(aServiceName);
	command.append(" restart");
	int result;
	result = system(command.c_str());
	if(result != 0)
	{
		command.insert(0,"/sbin/");
		result = system(command.c_str());
		if(result == 0)
			return true;
		else
			return false;
	}
	else
		return true;
}

#endif

ENDNS ENDNS
